package Backend;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Vector;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;

/**
 * Example listener for set methods
 *
 * listener = new ResponseListener() { public void onResponse(ResponseEvent
 * event) { PDU strResponse; String result; ((Snmp)
 * event.getSource()).cancel(event.getRequest(), this); strResponse =
 * event.getResponse(); if (strResponse != null) { System.out.println("Received
 * response PDU is: "+ strResponse); } } };
 */
public class SnmpManager {

    public static final String TIME_OUT = "TIMEOUT";
    private static int RETRIES = 2;
    private static int TIMEOUT = 1500; //milliseconds
    private Snmp snmp = null;
    private ArrayList<String> addresses = null;
    private ArrayList<String> communities = null;

    // constructor to use if adding addresses and communities later
    public SnmpManager() {
        addresses = new ArrayList<String>();
        communities = new ArrayList<String>();
    }

    // constructor to use if adding a single address and community
    public SnmpManager(String address, String community) {
        addresses = new ArrayList<String>();
        addresses.add(address);

        communities = new ArrayList<String>();
        communities.add(community);
    }

    // array constructor for multiple addresses and communities
    public SnmpManager(String[] add, String[] com) {
        addresses = new ArrayList<String>();
        addresses.addAll(Arrays.asList(add));

        communities = new ArrayList<String>();
        communities.addAll(Arrays.asList(com));
    }

    //arraylist constructor for multiple addresses and communities
    public SnmpManager(ArrayList<String> add, ArrayList<String> com) {
        addresses = add;
        communities = com;
    }

    // starts the manager
    public void start() throws IOException {
        TransportMapping transport = new DefaultUdpTransportMapping();
        snmp = new Snmp(transport);
        transport.listen(); // this line is super important
    }

    // This method returns a Target, compatible with version 2c of SNMP
    private Target getTarget(int index) {
        Address targetAddress = GenericAddress.parse(addresses.get(index));
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString(communities.get(index)));
        target.setAddress(targetAddress);
        target.setRetries(RETRIES);
        target.setTimeout(TIMEOUT);
        target.setVersion(SnmpConstants.version2c);
        return target;
    }

    // Grabs multiple OIDS from an agent at the specified index synchronously
    public ResponseEvent get(int index, OID oids[]) throws IOException {
        PDU pdu = new PDU();
        for (OID oid : oids) {
            pdu.add(new VariableBinding(oid));
        }
        pdu.setType(PDU.GET);
        ResponseEvent event = snmp.send(pdu, getTarget(index), null);
        if (event != null) {
            return event;
        } else {
            throw new IOException(TIME_OUT); // throw an IO Exception if the get times out
        }
    }

    // Grabs a single OID from an agent at the specified index synchronously
    public ResponseEvent get(int index, OID oid) throws IOException {
        ResponseEvent event = get(index, new OID[]{oid});
        return event;
    }

    // Grabs multiple OIDS from an agent at the specified index synchronously and returns their values
    public String[] getArrayStrings(int index, OID[] oids) throws IOException {
        ResponseEvent event = get(index, oids);

        PDU response = event.getResponse();
        if (response == null) {
            return null;
        } else if (response.getErrorStatus() != PDU.noError) {
            return new String[]{"Error=" + response.getErrorStatus()};
        }

        // convert response to string
        Vector<? extends VariableBinding> variableBindings = response.getVariableBindings();
        ArrayList<String> list = new ArrayList<String>();
        for (VariableBinding v : variableBindings) {
            list.add(v.toValueString());
        }

        return list.toArray(new String[1]);
    }

    // Grabs a single OID from an agent at the specified index synchronously and returns its value
    public String getString(int index, OID oid) throws IOException {
        ResponseEvent event = get(index, new OID[]{oid});

        PDU response = event.getResponse();

        if (response == null) {
            return null;
        } else if (response.getErrorStatus() != PDU.noError) {
            return "Error=" + response.getErrorStatus();
        }

        return response.getVariableBindings().firstElement().toValueString();
    }

    //asynchronous set method for an integer value, requires its own listener for response  
    public void set(int index, OID oid, int Value, ResponseListener listener) throws IOException {
        Target target = getTarget(index);
       
        PDU pdu = new PDU();
        pdu.add(new VariableBinding(oid, new Integer32(Value)));
        pdu.setType(PDU.SET);

        snmp.send(pdu, target, null, listener);
    }

    //asynchronous set method for a String value, requires its own listener for response
    public void set(int index, OID oid, String Value, ResponseListener listener) throws IOException {
        Target target = getTarget(index);
        PDU pdu = new PDU();
        //Depending on the MIB attribute type, appropriate casting can be done here
        pdu.add(new VariableBinding(oid, new OctetString(Value)));
        pdu.setType(PDU.SET);

        snmp.send(pdu, target, null, listener);
    }
}
